/*
    libnbds
    Non-blocking Data Structures Library

    Copyright (C) 2011 Paweł Dziepak

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "atomic.h"
#include "list.h"

using namespace nbds;

list_base::list_base() {
	null_element = new elem;
	null_element->prev = NULL;
	null_element->next = NULL;
	null_element->obj = NULL;

	head = null_element;
	tail = null_element;
}

list_base::~list_base() {
	elem *current = head;

	while (current) {
		elem *next = current->next;

		delete current;
		current = next;
	}
}

void list_base::push_back_(void *obj) {
	elem *x = new elem;
	x->obj = obj;
	x->next = NULL;

	elem *old_tail = NULL;

	do {
		old_tail = arch::get(&tail);
		x->prev = old_tail;

		if (old_tail && arch::get(&old_tail->next)) {
			arch::cmp_and_swp(&tail, old_tail,
						arch::get(&old_tail->next));
			continue;
		}

	} while (arch::cmp_and_swp(&old_tail->next, (elem*)0, x) != NULL);
	arch::cmp_and_swp(&tail, old_tail, x);
}

void list_base::push_front_(void *obj) {
	elem *x = new elem;
	x->obj = obj;
	x->prev = NULL;

	elem *old_head = NULL;

	do {
		old_head = head;
		x->next = old_head;

		if (old_head && arch::get(&old_head->prev)) {
			arch::cmp_and_swp(&head, old_head,
						arch::get(&old_head->prev));
			continue;
		}

	} while (arch::cmp_and_swp(&old_head->prev, (elem*)0, x) != NULL);
	arch::cmp_and_swp(&head, old_head, x);
}

bool list_base::is_empty_() {
	return !head;
}

list_base::iterator_base::iterator_base(elem *c, elem *h, elem *t, elem *ne) :
	current(c),
	head(h),
	tail(t),
	null_element(ne),
	cycle(false) { }

list_base::iterator_base::iterator_base(elem *h, elem *t, elem *ne) :
	current(h),
	head(h),
	tail(t),
	null_element(ne),
	cycle(false) { }

list_base::iterator_base::iterator_base(const iterator_base &x) :
				current(x.current),
				head(x.head),
				tail(x.tail),
				null_element(x.null_element),
				cycle(x.cycle) { }

list_base::iterator_base &list_base::iterator_base::operator=(const iterator_base &x) {
	current = x.current;
	head = x.head;
	tail = x.tail;
	null_element = x.null_element;
	cycle = x.cycle;

	return *this;
}

list_base::iterator_base &list_base::iterator_base::operator++() {
	elem *n = arch::get(&current->next);
	cycle = false;

	if (n == null_element)
		n = arch::get(&n->next);

	if (!n) {
		current = head;
		if (head == null_element && arch::get(&head->next))
			current = arch::get(&head->next);
		cycle = true;
	} else
		current = n;

	return *this;
}

list_base::iterator_base &list_base::iterator_base::operator--() {
	elem *n = arch::get(&current->prev);
	cycle = false;

	if (n == null_element)
		n = arch::get(&n->prev);

	if (!n) {
		current = tail;
		if (tail == null_element && arch::get(&tail->prev))
			current = arch::get(&tail->prev);
		cycle = true;
	} else
		current = n;

	return *this;
}

bool list_base::iterator_base::at_end() {
	return current == tail;
}

bool list_base::iterator_base::at_begin() {
	return current == head;
}

bool list_base::iterator_base::did_cycle() {
	return cycle;
}
